package redisclient

import (
	"context"
	"encoding/json"
	"time"

	"github.com/redis/go-redis/v9"
)

var Redis *redis.Client

/*
Init 初始化Redis
*/
func Init(conf *redis.Options) *redis.Client {
	return NewGeneralClient(conf)
}

// NewGeneralClient 單節點的client
func NewGeneralClient(conf *redis.Options) *redis.Client {
	Redis = redis.NewClient(conf)
	return Redis
}

/*
SetObj 保存任意对象（以json序列化保存）
*/
func SetObj[T any](key string, value *T) error {
	// 对象序列化
	marshal, err := json.Marshal(value)
	if err != nil {
		return err
	}
	// 保存到redis中
	return Redis.Set(context.Background(), key, string(marshal), 0).Err()
}

/*
SetexObj 保存对象（以json序列化保存），并设定超时时间
*/
func SetexObj[T any](key string, value *T, seconds int) error {
	// 对象序列化
	marshal, err := json.Marshal(value)
	if err != nil {
		return err
	}
	// 保存到redis中
	return Redis.Set(context.Background(), key, string(marshal), time.Duration(seconds)*time.Second).Err()
}

func Setkeepttl[T any](key string, value *T) error {
	marshal, err := json.Marshal(value)
	if err != nil {
		return err
	}

	return Redis.Set(context.Background(), key, string(marshal), redis.KeepTTL).Err()
}

/*
Expire 设定过期时间
*/
func Expire(key string, seconds int) error {
	return Redis.Expire(context.Background(), key, time.Duration(seconds)*time.Second).Err()
}

/*
GetObj 获取对象（json反序列化）
*/
func GetObj[T any](key string) (*T, error) {
	valueStr, err := Redis.Get(context.Background(), key).Result()
	if err != nil {
		return nil, err
	}

	var v T
	err = json.Unmarshal([]byte(valueStr), &v)
	if err != nil {
		return nil, err
	}

	return &v, nil
}

func GetObjFor[T any](key string, obj *T) error {
	valueStr, err := Redis.Get(context.Background(), key).Result()
	if err != nil {
		return err
	}

	err = json.Unmarshal([]byte(valueStr), obj)
	if err != nil {
		return err
	}

	return nil
}

// DeleteObj delete key from redis
func DeleteObj(key string) (int, error) {
	i, err := Redis.Del(context.Background(), key).Result()

	return int(i), err
}

func DeleteObjs(keys ...string) (int, error) {
	i, err := Redis.Del(context.Background(), keys...).Result()

	return int(i), err
}

// HSetObj 存一個hash結構， field重複的話會由新的覆蓋舊的
func HSetObj[T any](key, field string, value *T) error {
	b, err := json.Marshal(value)
	if err != nil {
		return err
	}

	m := map[string]string{
		field: string(b),
	}

	return Redis.HSet(context.Background(), key, m).Err()
}

func IsKeyExists(key string) (bool, error) {
	i, err := Redis.Exists(context.Background(), key).Result()
	if err != nil {
		return false, err
	}

	return i > 0, nil
}

// HGetAllFiledObjs 一次取得key的所有field跟value，value必須要是同一個型別，bool用來辨別key是否存在
func HGetAllFiledObjs[T any](key string) (bool, map[string]*T, error) {
	// HGETALL 如果key不存在, 不會有redis: nil的錯誤, 需要先檢查key是否存在
	keyExists, err := IsKeyExists(key)
	newMap := map[string]*T{}
	if err != nil || !keyExists {
		return false, newMap, err
	}

	result, err := Redis.HGetAll(context.Background(), key).Result()
	if err != nil {
		return false, nil, err
	}

	for field, v := range result {
		var t T
		if err := json.Unmarshal([]byte(v), &t); err != nil {
			return false, nil, err
		}

		newMap[field] = &t
	}

	return keyExists, newMap, nil
}

// HGetFieldObj 取得某個key中的某個field的value
func HGetFieldObj[T any](key, field string, obj *T) error {
	result, err := Redis.HGet(context.Background(), key, field).Result()
	if err != nil {
		return err
	}

	if err := json.Unmarshal([]byte(result), &obj); err != nil {
		return err
	}
	return nil
}

// HDelFieldObj 刪除某個hash內的某個field
func HDelFieldObj(key, field string) int {
	i, _ := Redis.HDel(context.Background(), key, field).Result()

	return int(i)
}

// GetKeys 取得key的列表, 支援wildcard
func GetKeys(pattern string) ([]string, error) {
	res, err := Redis.Keys(context.Background(), pattern).Result()
	if err != nil {
		return nil, err
	}

	return res, nil
}

// MGet 一次從多個key取得資料
func MGet[T any](keys ...string) ([]*T, error) {
	result, err := Redis.MGet(context.Background(), keys...).Result()
	if err != nil {
		return nil, err
	}

	l := make([]*T, 0)
	for _, r := range result {
		var tmp *T
		v, ok := r.(string)
		if !ok {
			continue
		}

		if err := json.Unmarshal([]byte(v), &tmp); err != nil {
			continue
		}

		l = append(l, tmp)
	}

	return l, nil
}

func ScanAndGet[T any](pattern string) ([]*T, error) {
	// 先用 GetKeys 取得所有符合的 keys
	keys, err := GetKeys(pattern)
	if err != nil {
		return nil, err
	}

	if len(keys) == 0 {
		return []*T{}, nil
	}

	// 使用現有的 MGet 取得值
	return MGet[T](keys...)
}

// HIncrby 針對hash內的feild做atomic操作
func HIncrby(key, field string, amount int) (int, error) {
	result, err := Redis.HIncrBy(context.Background(), key, field, int64(amount)).Result()
	if err != nil {
		return 0, err
	}

	return int(result), nil
}
